/**
 * Created : Mar 31, 2012
 *
 * @author pquiring
 *
 * BUG : Currently the check-boxes don't do anything.
 *
 */

import javax.swing.table.*;

import javaforce.*;
import javaforce.jbus.*;
import javaforce.linux.*;

public class MainPanel extends javax.swing.JPanel {

  /**
   * Creates new form MainPanel
   */
  public MainPanel() {
    initComponents();
    Linux.detectDistro();
    if (Linux.distro == Linux.DistroTypes.Unknown) {
      JFAWT.showError("Error", "Unsupported distro");
      System.exit(0);
    }
    if (Linux.distro == Linux.DistroTypes.Fedora) {
      upgradeAll.setVisible(false);
    }
    new Thread() {
      public void run() {
        listUpgrades();
      }
    }.start();
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    jScrollPane1 = new javax.swing.JScrollPane();
    packages = new javax.swing.JTable();
    upgrade = new javax.swing.JButton();
    check = new javax.swing.JButton();
    progress = new javax.swing.JProgressBar();
    status = new javax.swing.JTextField();
    upgradeAll = new javax.swing.JCheckBox();

    packages.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {

      },
      new String [] {
        "upgrade", "package", "desc"
      }
    ) {
      Class[] types = new Class [] {
        java.lang.Boolean.class, java.lang.String.class, java.lang.String.class
      };
      boolean[] canEdit = new boolean [] {
        true, false, false
      };

      public Class getColumnClass(int columnIndex) {
        return types [columnIndex];
      }

      public boolean isCellEditable(int rowIndex, int columnIndex) {
        return canEdit [columnIndex];
      }
    });
    jScrollPane1.setViewportView(packages);
    packages.getColumnModel().getColumn(0).setPreferredWidth(20);
    packages.getColumnModel().getColumn(1).setPreferredWidth(20);
    packages.getColumnModel().getColumn(2).setPreferredWidth(300);

    upgrade.setText("Upgrade");
    upgrade.setEnabled(false);
    upgrade.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        upgradeActionPerformed(evt);
      }
    });

    check.setText("Check");
    check.setEnabled(false);
    check.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        checkActionPerformed(evt);
      }
    });

    status.setEditable(false);

    upgradeAll.setText("Upgrade kept back");
    upgradeAll.setEnabled(false);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(progress, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
          .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 613, Short.MAX_VALUE)
          .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addGap(0, 0, Short.MAX_VALUE)
            .addComponent(upgradeAll)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(check)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(upgrade))
          .addComponent(status))
        .addContainerGap())
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 689, Short.MAX_VALUE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(progress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(status, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(upgrade)
          .addComponent(check)
          .addComponent(upgradeAll))
        .addContainerGap())
    );
  }// </editor-fold>//GEN-END:initComponents

  private void checkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkActionPerformed
    new Thread() {
      public void run() {
        check();
      }
    }.start();
  }//GEN-LAST:event_checkActionPerformed

  private void upgradeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_upgradeActionPerformed
    new Thread() {
      public void run() {
        upgrade();
      }
    }.start();
  }//GEN-LAST:event_upgradeActionPerformed

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton check;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JTable packages;
  private javax.swing.JProgressBar progress;
  private javax.swing.JTextField status;
  private javax.swing.JButton upgrade;
  private javax.swing.JCheckBox upgradeAll;
  // End of variables declaration//GEN-END:variables

  private final int NA = 0;
  private final int SHOULD = 1;
  private final int MUST = 2;

  private Object needReboot[][] = {
    {"linux-headers-.+", SHOULD},
    {"linux-image-.+", SHOULD},
    {"jdesktop", MUST},
    {"jlogon", MUST},
  };

  private void listUpgrades() {
    progress.setIndeterminate(true);
    status.setText("Getting list of available upgrades...");
    switch (Linux.distro) {
      case Ubuntu:
        ubuntu_listUpgrades();
        break;
      case Fedora:
        fedora_listUpgrades();
        break;
    }
    progress.setIndeterminate(false);
  }

  private void ubuntu_listUpgrades() {
    upgradeAll.setSelected(false);
    upgradeAll.setEnabled(false);
    ShellProcess sp = new ShellProcess();
    sp.removeEnvironmentVariable("TERM");
    sp.addEnvironmentVariable("DEBIAN_FRONTEND", "noninteractive");
    String output = sp.run(new String[] {"sudo", "-E", "apt-get", "--yes", "upgrade", "-s"}, false);
    String lns[] = output.split("\n");
    int keptBack = -1;
    int upgrades = -1;
    int cnts = -1;
    for(int a=0;a<lns.length;a++) {
      if (lns[a].startsWith("The following packages have been kept back")) {
        keptBack = a+1;
        continue;
      }
      if (lns[a].startsWith("The following packages will be upgraded")) {
        upgrades = a+1;
        continue;
      }
      if (lns[a].indexOf("upgraded") != -1 && lns[a].indexOf("newly installed") != -1 && lns[a].indexOf("to remove") != -1) {
        cnts = a;
        continue;
      }
    }
    if (cnts == -1) {
      status.setText("Error:Counts not found");
      return;
    }
    int i1 = lns[cnts].indexOf("and");
    int i2 = lns[cnts].indexOf("not");
    int notUpgradedCnt = JF.atoi(lns[cnts].substring(i1+3, i2).trim());
    int i3 = lns[cnts].indexOf(" ");
    int upgradedCnt = JF.atoi(lns[cnts].substring(0, i3));
    String str = "" + upgradedCnt + " upgrades available\n";
    if (notUpgradedCnt > 0) {
      str += " (" + notUpgradedCnt + " kept back)\n";
      upgradeAll.setEnabled(true);
    }
    status.setText(str);
    DefaultTableModel model = (DefaultTableModel)packages.getModel();
    model.setRowCount(0);
    if (upgrades != -1) {
      for(int a=upgrades;a<cnts;a++) {
        String pkgs[] = lns[a].trim().split(" ");
        for(int b=0;b<pkgs.length;b++) {
          model.addRow(new Object[] {true, pkgs[b], Linux.getPackageDesc(pkgs[b])});
        }
      }
    }
    check.setEnabled(true);
    upgrade.setEnabled(true);
    sendCounts(upgradedCnt);
  }

  private void fedora_listUpgrades() {
    upgradeAll.setSelected(false);
    upgradeAll.setEnabled(false);
    ShellProcess sp = new ShellProcess();
    sp.removeEnvironmentVariable("TERM");
    String output = sp.run(new String[] {"sudo", "-E", "yum", "--assumeno", "update"}, false);
    String lns[] = output.split("\n");
    int end = -1;
    int cnts = -1;
    boolean summary = false;
    for(int a=0;a<lns.length;a++) {
      if (lns[a].startsWith("Transaction Summary")) {
        end = a-1;
        summary = true;
      }
      if (!summary) continue;
      if (lns[a].startsWith("Upgrade")) {
        cnts = a;
      }
    }
    if (cnts == -1) {
      status.setText("No Updates found");
      check.setEnabled(true);
      return;
    }
    String f[] = lns[cnts].split(" +");  //greedy spaces
    int upgradedCnt = JF.atoi(f[1]);
    String str = "" + upgradedCnt + " upgrades available\n";
    status.setText(str);
    DefaultTableModel model = (DefaultTableModel)packages.getModel();
    model.setRowCount(0);
    boolean installing = false, updating = false;
    for(int a=0;a<end;a++) {
      if (lns[a].length() == 0) continue;
      if (lns[a].startsWith("Installing:")) {installing = true; continue;} //these are dependancies to updates (ignored)
      if (lns[a].startsWith("Installing for dependencies:")) {installing = true; continue;} //these are dependancies to updates (ignored)
      if (lns[a].startsWith("Updating:")) {updating = true; continue;}
      if (lns[a].startsWith("Transaction Summary")) break;
      if (!updating) continue;
      if (lns[a].startsWith("   ")) continue;  //continuation from last line
      String pkgs[] = lns[a].trim().split(" +");  //greedy spaces
      if (pkgs[0].trim().length() == 0) continue;
      model.addRow(new Object[] {true, pkgs[0], Linux.getPackageDesc(pkgs[0])});
    }
    check.setEnabled(true);
    upgrade.setEnabled(true);
    sendCounts(upgradedCnt);
  }

  private void check() {
    progress.setIndeterminate(true);
    status.setText("Checking for upgrades...");
    switch (Linux.distro) {
      case Ubuntu:
        ubuntu_check();
        break;
      case Fedora:
        fedora_check();
        break;
    }
    progress.setIndeterminate(false);
  }

  private void ubuntu_check() {
    check.setEnabled(false);
    upgrade.setEnabled(false);
    ShellProcess sp = new ShellProcess();
    sp.removeEnvironmentVariable("TERM");
    sp.addEnvironmentVariable("DEBIAN_FRONTEND", "noninteractive");
    String output = sp.run(new String[] {"sudo", "-E", "apt-get", "--yes", "update"}, true);
    if (output == null) {
      JFAWT.showError("Error", "Failed to exec apt-get");
      check.setEnabled(true);
      upgrade.setEnabled(true);
    } else {
      listUpgrades();
    }
  }

  private void fedora_check() {
    check.setEnabled(false);
    upgrade.setEnabled(false);
    ShellProcess sp = new ShellProcess();
    sp.removeEnvironmentVariable("TERM");
    String output = sp.run(new String[] {"sudo", "-E", "yum", "-y", "check-update", null}, false);
    if (output == null) {
      JFAWT.showError("Error", "Failed to exec yum");
      check.setEnabled(true);
      upgrade.setEnabled(true);
    } else {
      listUpgrades();
    }
  }

  private void upgrade() {
    progress.setIndeterminate(true);
    status.setText("Upgrading packages...");
    boolean ok = false;
    int action = NA;
    for(int a=0;a<packages.getRowCount();a++) {
      for(int b=0;b<needReboot.length;b++) {
        if (((String)needReboot[b][0]).matches((String)packages.getValueAt(a, 1))) {
          int value = (Integer)needReboot[b][1];
          if (value > action) action = value;
        }
      }
    }
    switch (Linux.distro) {
      case Ubuntu:
        ok = ubuntu_upgrade();
        break;
      case Fedora:
        ok = fedora_upgrade();
        break;
    }
    progress.setIndeterminate(false);
    ShellProcess sp = new ShellProcess();
    if (ok) {
      switch (action) {
        case NA:
          break;
        case SHOULD:
          if (JFAWT.showConfirm("Notice", "An important package was upgraded, you SHOULD reboot now.  Close all other apps before accepted")) {
            sp.run(new String[] {"sudo", "reboot"}, false);
          }
          break;
        case MUST:
          JFAWT.showMessage("Notice", "A critical package was upgraded, you MUST reboot now.  Close all other apps and press OK");
          sp.run(new String[] {"sudo", "reboot"}, false);
      }
    }
  }

  private boolean ubuntu_upgrade() {
    check.setEnabled(false);
    upgrade.setEnabled(false);
    ShellProcess sp = new ShellProcess();
    sp.removeEnvironmentVariable("TERM");
    sp.addEnvironmentVariable("DEBIAN_FRONTEND", "noninteractive");
    sp.run(new String[] {"sudo", "-E", "apt-get", "--yes", (upgradeAll.isSelected() ? "dist-upgrade" : "upgrade")}, true);
    if (sp.getErrorLevel() != 0) {
      JFAWT.showError("Error", "Failed to exec apt-get");
      check.setEnabled(true);
      upgrade.setEnabled(true);
      return false;
    } else {
      sp.run(new String[] {"sudo", "rm", "/etc/upgrade.cnts"}, true);
      check();
      return true;
    }
  }

  private boolean fedora_upgrade() {
    check.setEnabled(false);
    upgrade.setEnabled(false);
    ShellProcess sp = new ShellProcess();
    sp.removeEnvironmentVariable("TERM");
    String output = sp.run(new String[] {"sudo", "-E", "yum", "-y", "update", null}, false);
/*
    //TODO - get status code ??? assume ok for now
        if (sp.getErrorLevel() != 0) {
      JFAWT.showError("Error", "Failed to exec yum");
      check.setEnabled(true);
      upgrade.setEnabled(true);
      return false;
    } else {*/
      sp.run(new String[] {"sudo", "rm", "/etc/upgrade.cnts"}, true);
      check();
      return true;
//    }
  }

  private static void sendCounts(int upgrade) {
    JBusClient client = new JBusClient(null, null);
    client.start();
    client.call("org.jflinux.jfsystemmgr", "upgradesAvailable", "" + upgrade);
    client.close();
  }
}
